/*
 * This class represents a fraction of types representig a ring R
 * It is fundamental to make conversions between 0 and zero of the ring
 * and also 1 with the unity of the ring
 */


#ifndef _FRACTION_H
#define _FRACTION_H

#include<iostream>
#include<exception>

//Fraction related traits for simplification of the fraction

template<typename T>
class fraction_traits{
      public:
             //for fraction simplification
             static void simplify(T& t1, T& t2){
                    T div = gcd(t1, t2);
                    t1 = t1 / div;
                    t2 = t2 / div;
             }
             //To read the fraction from an input stream
             static std::istream& read(std::istream& is, T& t1, T& t2){
                    is>>t1;
                    if(!is.fail()){
                        //skipping spaces
                        int c;
                        while((c = is.get()) == ' ' && c != is.eof());
                        if(c == '/'){
                             is>>t2;
                        }
                    }
                    return is;
             }
};

 
template<typename R, typename traits = fraction_traits<R> >
class fraction{
      public:
             fraction():numerator(R(0)),denominator(R(1)){}
             fraction(R r):numerator(r),denominator(R(1)){}
             fraction(R num, R den);  //Must handle division by zero
             fraction(const fraction& f):numerator(f.numerator),denominator(f.denominator){}
             fraction(std::istream& is);
             
             //fraction(int i):numerator(R(i)),denominator(R(1)){}  //A fraction constructed from integer
             
             fraction operator+(fraction& right);
             fraction operator-(fraction& right);
             fraction operator/(fraction& right);
             fraction operator*(fraction& right);  //Must handle division by zero
             fraction operator*(R& right);
             fraction& operator=(fraction& right);
             bool operator==(fraction& right);
             bool operator!=(fraction& right);
             
             template<typename T, typename Traits>
             friend std::istream& operator>>(std::istream& is, fraction<T,Traits>& f);
             template<typename T, typename Traits>
             friend std::ostream& operator<<(std::ostream& os, fraction<T,Traits> f);
             
             R get_numerator(){return numerator;}
             R get_denominator(){return denominator;}
             //T as_integer(){return numerator/denominator;}
             
             fraction power(int pow);
             
             //field specifics
             fraction inverse();
             fraction symmetric();  //Must handle division by zero
             
             //Handling fraction exceptions
             class fraction_exception: public std::exception{
                 virtual const char* what() const throw(){
                     return "Division by zero.";
                 }
             };
             
      private:
              R numerator;
              R denominator;
              
              std::istream& read(std::istream& is);
              std::ostream& write(std::ostream& os);
              //It should be possible to calculate the gcd for rings
              //R gcd(R& r1, R& r2);
              //gcd is a particular property of the ring and should be considered as a 
              //separated algorithm in this case
              void simplify();
};
#endif
